#ifndef __W_ROOM_H_
#define __W_ROOM_H_
#include "online.hpp"
#include "db.hpp"

typedef enum { GAME_START, GAME_OVER} room_statu;   //游戏状态
//棋盘长宽
#define BOARD_ROW 15    //棋盘长
#define BOARD_COL 15    
// 棋子颜色
#define CHESS_WHITE 1
#define CHESS_BLACK 2
class room
{
public:
    room(uint64_t room_id, user_table *tb_user, online_manager *online_user)
        :_room_id(room_id), _statu(GAME_START), _player_count(0), _tb_user(tb_user),
        _online_user(online_user),_board(BOARD_ROW, vector<int>(BOARD_COL, 0))
    {
        DLOG("%lu 房间创建成功！", _room_id);
    }
    ~room()
    {
        DLOG("%lu 房间销毁成功！", _room_id);
    }
    uint64_t get_room_id() { return _room_id; }     //获取房间id
    room_statu get_room_statu() { return _statu; }  //获取房间状态
    int get_player_count() { return _player_count; }    //获取房间中玩家数量
    void add_white_id(uint64_t uid) { _white_id = uid; _player_count++; }   //添加白棋用户id
    void add_black_id(uint64_t uid) { _black_id = uid; _player_count++; }   //添加黑棋用户id
    uint64_t get_white_id() { return _white_id; }   //获取白棋用户id
    uint64_t get_black_id() { return _black_id; }   //获取黑棋用户id

    // 处理下棋动作
    Json::Value handle_chess(Json::Value& req)
    {
        Json::Value json_resp = req;

        //  判断房间中两个玩家是否都在线，任意一个不在线，另一方胜利
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        uint64_t cur_uid = req["uid"].asUInt64();
        if(_online_user->is_in_game_room(_white_id) == false)   //白棋用户掉线
        {
            json_resp["result"] = true;
            json_resp["reason"] = "对方玩家掉线，游戏胜利！";
            json_resp["winner"] = (Json::UInt64)_black_id;

            return json_resp;
        }
        if(_online_user->is_in_game_room(_black_id) == false)   //黑棋用户掉线
        {
            json_resp["result"] = true;
            json_resp["reason"] = "对方玩家掉线，游戏胜利！";
            json_resp["winner"] = (Json::UInt64)_white_id;

            return json_resp;
        }
        // 3. 获取走棋位置，判断走棋位置是否合理，如位置被占用就不能下了
        if(_board[chess_row][chess_col] != 0)   //该位置有其他的棋子
        {
            json_resp["result"] = false;
            json_resp["reason"] = "该位置已有棋子！";
            return json_resp;
        }
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;   //当前棋子颜色
        _board[chess_row][chess_col] = cur_color; //当前下棋用户是谁，位置就是谁的棋子

        // 4. 判断当前是否有玩家胜利了
        uint64_t winner_id  = check_win(chess_row, chess_col, cur_color);
        if(winner_id != 0)  //证明有玩家胜利了
        {   
            json_resp["reason"] = "五子连线，游戏胜利！";

        }
        json_resp["result"] = true;
        json_resp["winner"] = (Json::UInt64)winner_id;
        return json_resp;
    }
    // 处理聊天动作
    Json::Value handle_chat(Json::Value& req)
    {
        Json::Value json_resp = req;
        // 检查有没有敏感词汇
        string message = req["message"].asString();
        if(message.empty())   //消息为空也是不能发送的
        {
            json_resp["result"] = false;
            json_resp["reason"] = "消息为空，不能发送！";
            return json_resp;
        }
        auto pos = message.find("傻逼");
        if(pos != string::npos) //有敏感词不能发送
        {
            json_resp["result"] = false;
            json_resp["reason"] = "消息中包含敏感词，不能发送！";
            return json_resp;
        }
        //  广播消息 -> 返回消息
        json_resp["result"] = true;
        return json_resp;
    }
    // 处理玩家退出房间动作
    void handle_exit(uint64_t uid)
    {
        Json::Value json_resp;
        // 1. 如果是下棋中退出，则对方胜利，如果游戏结束退出，则是正常退出
        if(_statu == GAME_START)
        {
            uint64_t winner_id = (Json::UInt64)(uid == _white_id ? _black_id : _white_id);
            uint64_t loser_id = winner_id == _white_id ?_black_id : _white_id;

            json_resp["optype"] = "put_chess";
            json_resp["result"] = true;
            json_resp["reason"] = "对方玩家退出房间，游戏胜利！";
            json_resp["room_id"] = (Json::UInt64)_room_id; 
            json_resp["uid"] = (Json::UInt64)uid; 
            json_resp["row"] = -1;
            json_resp["col"] = -1;
            json_resp["winner"] = (Json::UInt64)winner_id;

            _tb_user->win(winner_id);
            _tb_user->lose(loser_id);
            _statu = GAME_OVER;
            
            boardcast(json_resp);
        }

        // 2.游戏房间内玩家数量--
        _player_count--;
        return ;
    }
    // 总的请求函数，在函数内部区分请求类型，根据不同请求调用不同的处理函数，得到响应后进行广播
    void handle_request(Json::Value& req)
    {
        Json::Value json_resp;
        // 1. 校验房间号，检查房间是否一致
        uint64_t room_id = req["room_id"].asUInt64();
        if(room_id != _room_id)
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "房间号不匹配！";
            return boardcast(json_resp);
        }
        // 根据不同请求调用不同的处理函数
        if(req["optype"].asString() == "put_chess") //下棋请求
        {
            json_resp = handle_chess(req);
            if(json_resp["winner"].asUInt64() != 0) //记得加asUInt64()，不加会出现天梯积分和对战总场次混乱情况
            {
                // 更新数据库用户信息
                uint64_t winner_id = json_resp["winner"].asUInt64();
                uint64_t loser_id = winner_id == _white_id ?_black_id : _white_id;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _statu = GAME_OVER;
            } 
        }
        else if(req["optype"].asString() == "chat")//聊天请求
        { 
            json_resp = handle_chat(req); 
            
        }
        else    
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "未知请求类型！";            
        }
        return boardcast(json_resp);
    }
    // 将指定信息广播给房间中的所有玩家
    void boardcast(Json::Value& rsp)
    {
        string body;
        // 1. 对要响应的信息进行序列化
        if(json_util::serialize(rsp, body) == false)
        {
            return;
        }

        // 2. 获取房间所有用户的通信链接
        // 3. 发送响应消息
        wsserver_t::connection_ptr wconn = _online_user->get_conn_from_room(_white_id);
        if(wconn.get() != nullptr)
        {
            wconn->send(body);
        }
        wsserver_t::connection_ptr bconn = _online_user->get_conn_from_room(_black_id);
        if(bconn.get() != nullptr)
        {
            bconn->send(body);
        }
        return;
    }
    
private:
    //判断是否有五子连线
    bool five(int row, int col, int row_off, int col_off, int color)
    {
        // 判断某个坐标的偏移位置中是否存在五子连线
        int count = 1;
        int search_row = row + row_off;
        int search_col = col + col_off;
        while( search_row >= 0 && search_row < BOARD_ROW 
            && search_col >= 0 && search_col < BOARD_COL
            && _board[search_row][search_col] == color)
        {
            count++;
            search_row += row_off;
            search_col += col_off;
        }
        search_row = row - row_off;
        search_col = col - col_off;
        while( search_row >= 0 && search_row < BOARD_ROW 
            && search_col >= 0 && search_col < BOARD_COL
            && _board[search_row][search_col] == color)
        {
            count++;
            search_row -= row_off;
            search_col -= col_off;
        }

        return count >= 5;
    }
    //检查是否有胜利者
    uint64_t check_win(int row, int col, int color)
    {
        //行，列，两个对角线有任意一条线连成五子，即为胜利
        if(five(row, col, 0, 1, color) || five(row, col, 1, 0, color)
        || five(row, col, 1, 1, color) || five(row, col, 1, -1, color))
        {
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
        return 0;
    }
private:
    uint64_t _room_id;  //房间id
    room_statu _statu;  //房间状态
    int _player_count;  //房间内的玩家数量
    uint64_t _white_id; //白棋玩家id
    uint64_t _black_id; //黑棋玩家id
    user_table* _tb_user;   //数据库表的访问模块的句柄
    online_manager* _online_user;   //在线用户管理模块的操作句柄
    vector<vector<int>> _board;
};

using room_ptr = shared_ptr<room> ;
class room_manager
{
public:
    // 初始化房间id计数器
    room_manager(user_table* tb_user, online_manager* online_user)
        :_next_rid(1), _tb_user(tb_user), _online_user(online_user)
    {   DLOG("房间管理模块初始化完成！"); }
    ~room_manager(){  DLOG("房间管理模块销毁完成！"); }
    // 为两个用户创建房间，并返回房间的智能指针管理对象
    room_ptr create_room(uint64_t uid1, uint64_t uid2)
    {
        // 两个用户在游戏大厅通过对战匹配，匹配成功后创建房间
        // 1. 校验两个用户在游戏大厅是否都是在线状态
        if(_online_user->is_in_game_hall(uid1) == false)
        {
            DLOG("用户：%lu 当前不在线，房间创建失败！", uid1);
            return room_ptr();
        }
        if(_online_user->is_in_game_hall(uid2) == false)
        {
            DLOG("用户：%lu 当前不在线，房间创建失败！", uid2);
            return room_ptr();
        }
        // 2. 创建房间，将用户信息添加到房间
        unique_lock<mutex> lock(_mutex);
        room_ptr rp(new room( _next_rid, _tb_user, _online_user));
        rp->add_white_id(uid1);
        rp->add_black_id(uid2);
        // 3. 将房间信息管理起来
        _rooms.insert(make_pair(_next_rid, rp));
        _users.insert(make_pair(uid1, _next_rid));
        _users.insert(make_pair(uid2, _next_rid));
        _next_rid += 1; //房间号自增，不能让房间号都重复
        // 4. 返回房间信息
        return rp;
    }
    // 通过房间id获取房间信息
    room_ptr get_room_by_rid(uint64_t rid)
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _rooms.find(rid);
        if(it == _rooms.end())
        {
            return room_ptr();
        }
        return it->second;
    }
    // 通过用户id获取房间信息
    room_ptr get_room_by_uid(uint64_t uid)
    {
        uint64_t rid = 0;
        {
            unique_lock<mutex> lock(_mutex);
            auto it = _users.find(uid);
            if(it == _users.end())
            {
                return room_ptr();
            }
            rid = it->second;
        }
        
        return get_room_by_rid(rid);
    }
    // // 通过用户id获取房间信息
    // room_ptr get_room_by_uid(uint64_t uid)
    // {
    //     unique_lock<mutex> lock(_mutex);
    //     auto uit = _users.find(uid);
    //     if(uit == _users.end())
    //     {
    //         return room_ptr();
    //     }
    //     auto rit = _rooms.find(uit->second);
    //     if(rit == _rooms.end())
    //     {
    //         return room_ptr();
    //     }
    //     return rit->second;
    // }

    // 通过房间id销毁房间
    void remove_room(uint64_t rid)
    {
        // 因为房间信息是通过shared_ptr在_rooms里进行管理，因此只要将shared_ptr在_rooms中移除，
        // shared_ptr的计数器为0，房间就销毁了
        // 房间销毁，房间中的用户信息也需要被处理，也需要将用户信息从_users中移除
        // 1. 通过房间id获取房间信息
        room_ptr rp = get_room_by_rid(rid);
        if(rp.get() == nullptr) //如果为空，证明没有该房间信息
        { return; }
        // 2. 通过房间信息获取房间中所有用户的id
        uint64_t uid1 = rp->get_white_id();
        uint64_t uid2 = rp->get_black_id();

        // 3. 移除房间中管理的用户信息
        unique_lock<mutex> lock(_mutex);
        _users.erase(uid1);
        _users.erase(uid2);
        // 4. 移除房间管理信息
        _rooms.erase(rid);
    }
    // 删除房间中的指定用户，房间中没有用户就销毁房间，用户链接被断开时调用
    void remove_room_user(uint64_t uid)
    {
        room_ptr rp = get_room_by_uid(uid);
        if(rp.get() == nullptr) //如果为空，证明没有该房间信息
        { return; }
        rp->handle_exit(uid);   //指定用户退出房间
        if(rp->get_player_count() == 0) //房间中没有玩家了，销毁房间
        {
            remove_room(rp->get_room_id());
        }
        return;
    }   
private:
    uint64_t _next_rid; //房间ID计数器
    mutex _mutex;
    user_table* _tb_user;       //数据库表的访问模块的句柄
    online_manager* _online_user;   //在线用户管理模块的操作句柄
    unordered_map<uint64_t, room_ptr> _rooms;   //rid 和 room 对象的关联关系
    unordered_map<uint64_t, uint64_t> _users;   //rid 和 uid 的关联关系

};

#endif 